home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Hardcore Visual Basic 5.0 (2nd Edition)
/
Hardcore Visual Basic 5.0 - Second Edition (1997)(Microsoft Press).iso
/
Code
/
Goodies
/
CallBack
/
CALLBACK.TXT
< prev
next >
Wrap
Text File
|
1997-06-10
|
15KB
|
337 lines
***********************************
What is the VB Callback Server Dll?
***********************************
The Visual Basic Callback Server Dll allows you to use methods in a VB
class module as callbacks for standard Windows API calls. Exposing
callback entry points in Visual Basic allows you to easily use the
EnumWindows, EnumFonts, and other windows enumeration functions
without writing a C Dll to handle the function pointers required for
these API calls. Other applications include timers without forms,
controlling a dialog resource from VB, and installing Windows hooks.
VBA is not thread-safe, so callbacks for threading aren't supported.
Since window procedures are considered callback functions, the
callback server can also be used to subclass windows (in the same
process, cross-process subclassing isn't supported). You can also use
the Message Blaster OCX for subclassing. While MsgBlast is easier to
use for the programmer making an early attempt at directly
manipulating windows message and supports cross-process subclassing,
the callback server has much less overhead. MsgBlast doesn't support
callbacks, just subclassing of a window handle.
******
Setup?
******
To install the callback server, copy CBack32.Dll to the Windows\System
directory (or System32 on Windows NT) and run regsvr32 CBack32.Dll.
Regsvr32 can be found in the Tools\RegUtils directory on the VB5 CD. If you
will be using the callback server to subclass and will be using break
mode in VB, you should also copy VBBrk32.Dll to the system[32]
directory. VBBrk32.Dll isn't required (in fact, it will not load) for
a VB executable. VBBrk32 failing to load will cause Regsvr32 to post
an error message, but you won't get this error when using CBack32 from
a VB executable. If you want to insure CBack32 is properly installed
on your client machine, add the following code to your program
(assuming you have a reference to VB CallBack Server).
Private Declare Function DllRegisterServer Lib "CBack32.Dll" () As Long
Sub Main()
Dim CBackGen As CallBackGenerator
On Error Resume Next
Set CBackGen = New CallBackGenerator
If Err Then
DllRegisterServer
Err.Clear
Set CBackGen = New CallBackGenerator
End If
If Err Then MsgBox "This program requires CBack32.Dll": End
End Sub
****************
How do I use it?
****************
To help you understand how to create a callback class module, we'll
step through creating a class module to provide an EnumWindowsProc
callback for the EnumWindows API call. We'll go over the details of
each of the calls after you've seen the initial steps:
1. Add a reference to the Callback Server to your project.
2. Insert a class module into your project.
3. In the object browser, look at the VBCallBackType enumeration in
the VBCallBack type library.
4. Select CBType_WNDENUMPROC and look at the description of the
required method. You'll see (ByVal Long, Any) As Long. This
information, together with the windows API help file for EnumWindows,
will help you create a correct function prototype.
5. Make sure your editor is in Full Module View. You can change this
setting using the Editor tab of the Options dialog.
6. Type the following into your class module:
Public Function WNDENUMPROC(ByVal hWnd As Long, LParam As ListBox) As Long
(The last parameter is user defined, we'll pass in a listbox just
because we can).
7. Before the first method, type:
Private m_CallBack As CallBack
8. Add a Class_Initialize event. In the Class_Initialize event, type
Set m_CallBack = NewCallBack(CBType_WNDENUMPROC, Me, True)
9. Add a read-only ProcAddress property as follows:
Public Property Get ProcAddress() As Long
ProcAddress = m_CallBack.ProcAddress
End Property
You're now ready to create an instance of your class. After you create
an instance of the class, you can pass the ProcAddress property of
your class to the EnumWindows API call, and EnumWindows will call your
WNDENUMPROC method.
************************************
What is the first method of a class?
************************************
The first method of a class is the first public entry in a class
module. The callback server will only work if the first method is
correctly defined. Public variables aren't allowed before the first
method. To make sure the method you're defining is listed back first,
be sure to use Full Module View. If you screw this up, you'll crash.
VBHandler:
The VBHandler property of the CallBack class is used to set the VB
class instance which will be used to handle the callback.
ProcAddress:
The ProcAddress property returns the address you should use as the
function pointer for your VB class.
Persistent ProcAddress, IsPersistent:
Some callback addresses are used by a single API call. For example,
you only have to count on the address of an enum procedure for the
duration of the enumeration call. Other addresses, however, must
remain constant for an indefinite amount of time. For example, a
WindowProc stays the same as long as the window remains alive and
is a persistent callback type. You can use the IsPersistent
property of a callback object to see if the VBHandler class you
assigned will always be called for the given procaddress. If a
callback is persistent, then you can only create 32 instances of
the callback object (32 was just as easy as 16).
ResetCallBack:
If a callback address isn't persistent, then the same address is
used for each instance of a callback with a given type. This means
that the class instance which is being pointed to by the internal
callback routine may be invalid if you have more than one instance
of the same non-persistent callback type. Reading the ProcAddress
property or calling the ResetCallBack method will guarantee that
the next call will be mapped to the correct class instance. For
example, if you are enumerating top-level windows and se a second
class to enumerate child windows, you will need to do an
m_CallBack.ResetCallBack before you return from the outer
EnumWindows call. If you don't, then the next call made by
EnumWindows will actually call the wrong enumerator class. In the
sample code, this isn't an issue because the same enumerator is
used recursively for top-level and child windows.
NewCallBack:
The NewCallBack method of the CallBackGenerator class is used to
create a callback object. You can't use the New keyword directly to
create a callback object. The first parameter, which is required,
specifies the type of callback object to be created. Note that
NewCallBack will fail if you ask for more than 32 instances or a
persistent callback type. The valid values for the Type parameter
are listed in the VBCallBackType enumeration. If the callback type
you use doesn't match your first method, you'll crash. The
VBHandler optional parameter is used as a shortcut for setting the
VBHandler property. Pass in a reference to your class module. The
Contained optional parameter, which takes a boolean value, is a
little harder to understand. If Contained is true, then NewCallBack
does not increment the reference count (ie, AddRef), the object
specified by the VBHandler parameter. This enables you to create a
class module which uses a callback object and can be destroyed with
a Set obj=Nothing call. If you don't use the contained flag, a Quit
(or similar method) is required to clean up the object correctly.
If you try to persist the callback object beyond the lifetime of
the initial VBHandler, you must reset VBHandler. If you don't, you
will crash. Normally, the call to NewCallBack is made in the
Class_Initialize event and looks like:
Set m_CallBack = NewCallBack(CBType_WNDPROC, Me, True)
*****************
How does it work?
*****************
The concept behind the callback server is very simple. Visual Basic
classes are interfaces which are derived from IDispatch. A VB class
has a VTable, and you can generate a C++ header file which describes
the VTable. Given an instance of a VB class, you can call its methods
from C++ using the VTable. For example, the class for the WNDPROC
looks something like:
class CVBWNDPROC : IDispatch
{
STDMETHOD(CB_WNDPROC)(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam, LRESULT FAR*) = 0;
};
Reliance on the VTable is the reason why correctly defining the first
method of the VB class is so important. The callback server doesn't
care what the remaining methods and properties are, so long as the
first method matches the class.
In order to have a callback, you must have a procedure address. Since
member functions of classes can't be used reliably as function
pointers (the implicit this pointer screws things up), the callback
Dll has actual entry points for each defined callback type. The
addresses of these fixed entry points are returned to the ProcAddress
property of the callback class. These procedures simply call through
the VTable into the VB class. For persistent callback types, there are
32 such entry points. Lets look at the first one:
LRESULT WINAPI CB_WNDPROC0 (HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
LRESULT retVal = 0;
//Break mode checks, see next section
if (*VBInBreakMode && VBInBreakMode() ) {
if ( pCWNDPROC[0]->m_pDebugProc )
return (pCWNDPROC[0]->m_pDebugProc)(hwnd, msg, wParam, lParam);
else
return 0;
}
//The real call. pCWNDPROC is a global array of pointers to
//CallBack classes.
if (pCWNDPROC[0]->m_pVBClass)
((CVBWNDPROC FAR*) (pCWNDPROC[0]->m_pVBClass))->
CB_WNDPROC (hwnd, msg, wParam, lParam, &retVal);
return retVal;
}
***************************
What happens in break mode?
***************************
Visual Basic doesn't mind it when you call directly into VTables,
unless it happens to be in break mode. If VB is in break mode, it
doesn't expect any VB code to be executed. Calling the VTable directly
bypasses all safety checks to stop code from executing, so the
callback server must check this for you. The functionality for this is
provided in a separate Dll called VBBrk32.Dll. This Dll exposes one
entry point, called VBInBreakMode, which returns a BOOL. The Dll will
fail to load if the callback server isn't running in the Visual Basic
design environment. You can get a pointer to VBInBreakMode using
LoadLibrary and GetProcAddress calls. By declaring a variable as
follows:
BOOL (WINAPI *VBInBreakMode)(void) = NULL;
You can use the code shown above to determine if VB is in break mode
or not.
DebugProc:
So what happens when VB is in break mode? The answer is simple: the
VTable call is disallowed. If the callback is being used to
subclass, this causes a major problem because no windows message
get through. To work around this, the callback class has a
DebugProc property which can be set to the function pointer which
is called in place of the VTable call when VB is in break mode.
Search the sample code for DebugProc to see this in action.
*****************
Debugging Crashes
*****************
Since you are making VTable calls, you have to be very careful that
your VTable and parameters are set up correctly. If you are
experiencing crashes when your callback method is called, try to put a
break point on the first (ie, the Sub or Function line) of the
callback method. If you don't reach the breakpoint before crashing,
then you've misplaced the first method. Be sure there are no public
methods, properties, or variables listed before your callback method.
If you reach the break point, but crash if you step off of it, then
your parameters are wrong.
If you are running the 16 bit callback server, you may run into
conflicts with VB when the Appearance property of controls on a
subclassed form is set to 1 - 3D. This isn't a problem for the 32 bit
server.
******************************************
Workarounds for parameter type limitations
******************************************
VB classes can't take structures as parameters. This causes some
problems for some callbacks, which pass in structures. When your
callback takes a structure, define the parameter as a ByVal Long and
use CopyMemory to make a copy of the structure. See WM_GETMINMAXINFO
or EnumFontFamProc in the sample project.
Another problem occurs with strings. VB strings are actually BSTRs,
but callback strings are passed in as LPSTRs. You can use CopyMemory
to make a copy of the string. See the ResourceEnumerator class in the
sample for an example.
********************************
How to build the callback server
********************************
The code for the callback server is included. Everything is highly
macroized, so you need to modify only the CBackDat.h file to add or
remove callbacks. Beneath the main directory, there are IP32 and IP16
(IP=In-process, I use this for all of my servers). The IP directories
contain the VC mak files. In order to build the project, you first
need to build the TLB and header files using TLB.Bat, which should be
called from the IP32 directory with ..\TLB 32 H before you attempt to
build the 32 bit callback server.
Although I've tried to include as many callbacks in the server as I
could find, you may find some that I missed. You may also notice that
the Dll could be smaller because I have many more callbacks defined
than you can ever use in a single project. If you wish to build a
custom version of the callback server, feel free to do so. However,
please call the server something else, rename the file, and change the
GUIDs in CBack.ODL before redistribution of your server.
The code for the VBBrk32 project isn't included. This Dll will need to
be replaced for future version of Visual Basic (hence the generic
name). Any code you derive from reverse engineering this Dll is
explicitly not supported. You can use it the same way I used it and
get the same results.
*********************
Copyright information
*********************
CBack32.Dll and CBack16.Dll can be redistributed freely, as can
derivations of the server. However, the sample code and rights to the
callback server belong to Microsoft Corporation and are copyrighted
with this book. Redistribution of the sample code is prohibited.
I hope you enjoy using my callback server. I'd love to hear your
feedback, as well as feature requests for future versions of the
callback server Dll. You can contact me MattCur@Microsoft.Com.
Matthew Curland